//////////////////////////////////////////////////////
// 程序名称：交互操作
// 功 能：为实验一提供交互平台
// 编译环境：Visual Studio 2017，EasyX_20190219(beta)
// 作 者：xyqlx<mxxyqlx@qq.com>
// 最后修改：2019-3-14
#include <conio.h>
#include <graphics.h>
#include <cmath>

COLORREF currentColor = RGB(255, 0, 0);

void MPLline(int x1,int y1,int x2,int y2,COLORREF color){
	int a,b,d1,d2,d,x,y;
	if(x1>x2){
		x1 ^= x2;
		x2 ^= x1;
		x1 ^= x2;
		y1 ^= y2;
		y2 ^= y1;
		y1 ^= y2;
	}
	x = x2 - x1;
	y = y2 - y1;
	if(y >= x){
		a=x1-x2;b=y2-y1;
		d=2*a+b;
		d1=2*(a+b);
		d2=2*a;
		x=x1;
		y=y1;
		putpixel(x,y,color);
		while(y<y2)
		{
			if(d<0) {x++;y++; d+=d1;}
			else {y++; d+=d2;}
			putpixel(x,y,color);
		}
	}
	else if(y <= x && y >= 0){
		a=y1-y2;b=x2-x1;
		d=2*a+b;
		d1=2*(a+b);
		d2=2*a;
		x=x1;
		y=y1;
		putpixel(x,y,color);
		while(x<x2)
		{
			if(d<0) {x++;y++; d+=d1;}
			else {x++; d+=d2;}
			putpixel(x,y,color);
		}
	}
	else if(y>=-x){
		a=y2-y1;b=x2-x1;
		d=2*a+b;
		d1=2*(a+b);
		d2=2*a;
		x=x1;
		y=y1;
		putpixel(x,y,color);
		while(x<x2)
		{
			if(d<0) {x++;y--; d+=d1;}
			else {x++; d+=d2;}
			putpixel(x,y,color);
		}
	}
	else{
		a=x1-x2;b=y1-y2;
		d=2*a+b;
		d1=2*(a+b);
		d2=2*a;
		x=x1;
		y=y1;
		putpixel(x,y,color);
		while(y>y2)
		{
			if(d<0) {x++;y--; d+=d1;}
			else {y--; d+=d2;}
			putpixel(x,y,color);
		}
	}
}

void MidpointCircle(int x0,int y0,int r, COLORREF color)
{
	int x, y, d;
	x = 0;
	y = r;
	d = 1 - r;
	putpixel(x + x0, y + y0, color);
	putpixel(x + x0, -y + y0, color);
	putpixel(y + x0, x + y0, color);
	putpixel(y + x0, -x + y0, color);
	putpixel(-x + x0, y + y0, color);
	putpixel(-x + x0, -y + y0, color);
	putpixel(-y + x0, x + y0, color);
	putpixel(-y + x0, -x + y0, color);
	while (x < y)
	{
		if (d < 0)
		{
			d += 2 * x + 3;
			x++;
		}
		else
		{
			d += 2 * (x - y) + 5;
			x++;
			y--;
		}
		putpixel(x + x0, y + y0, color);
		putpixel(x + x0, -y + y0, color);
		putpixel(y + x0, x + y0, color);
		putpixel(y + x0, -x + y0, color);
		putpixel(-x + x0, y + y0, color);
		putpixel(-x + x0, -y + y0, color);
		putpixel(-y + x0, x + y0, color);
		putpixel(-y + x0, -x + y0, color);
	}
}

void MidpointStdEllipse(int x0,int y0,int a,int b,COLORREF color){
	//计算必要参数
	int x, y, d;
	x = 0;
	y = b;
	int px = a * a / sqrt(a * a + b * b);
	int py = b * b / sqrt(a * a + b * b);
	d = b*b+(-b+0.25)*a*a;
	putpixel(x + x0, y + y0, color);
	putpixel(x + x0, -y + y0, color);
	putpixel(-x + x0, y + y0, color);
	putpixel(-x + x0, -y + y0, color);
	while (x <= px)
	{
		if (d < 0)
		{
			d += (2*x+3)*b*b;
			x++;
		}
		else
		{
			d += (2*x+3)*b*b+(-2*y+2)*a*a;
			x++;
			y--;
		}
		putpixel(x + x0, y + y0, color);
		putpixel(x + x0, -y + y0, color);
		putpixel(-x + x0, y + y0, color);
		putpixel(-x + x0, -y + y0, color);
	}
	d = 1.0f * b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b;
	while (y > 0)
	{
		if (d < 0)
		{
			d += (-2*y+3)*a*a+(2*x+2)*b*b;
			x++;
			y--;
		}
		else
		{
			d += (-2*y+3)*a*a;
			y--;
		}
		putpixel(x + x0, y + y0, color);
		putpixel(x + x0, -y + y0, color);
		putpixel(-x + x0, y + y0, color);
		putpixel(-x + x0, -y + y0, color);
	}
}

class Shape{
public:
	Shape(int n):stateNum(n){}
	virtual void Redraw()=0;
	int stateNum;
	virtual void State(int state,int x,int y)=0;
};

class Line:public Shape{
public:
  int x1, y1, x2, y2;
  COLORREF lineColor;
  Line():Shape(4){}
  virtual void Redraw()
  {
	  MPLline(x1, y1, x2, y2, currentColor);
  }
  virtual void State(int state,int x,int y){
	  switch(state){
		  case 1:
				x1 = x;
				y1 = y;
				break;
		  case 2:
			  	x2 = x;
			  	y2 = y;
				lineColor = currentColor;
		  case 3:
			  MPLline(x1, y1, x2, y2, lineColor);
			  break;
	  }
  }
};

class Circle:public Shape{
public:
  int x0, y0, r;
  COLORREF lineColor;
  Circle():Shape(4){}
  virtual void Redraw()
  {
	  MidpointCircle(x0, y0, r, lineColor);
  }
  virtual void State(int state,int x,int y){
	  switch(state){
		  case 1:
			  MPLline(x - 5, y, x + 5, y, currentColor);
			  MPLline(x, y - 5, x, y + 5, currentColor);
			  x0 = x;
			  y0 = y;
			  break;
		  case 2:
			  r = sqrt(pow(x - x0, 2) + pow(y - y0, 2));
			  lineColor = currentColor;
		  case 3:
				MidpointCircle(x0, y0, r, lineColor);
				break;
	  }
  }
};

class SEllipse:public Shape{
public:
  int x0, y0, a,b;
  COLORREF lineColor;
  SEllipse():Shape(6){}
  virtual void Redraw()
  {
	  MidpointStdEllipse(x0, y0, a, b, lineColor);
  }
  virtual void State(int state,int x,int y){
	  switch(state){
		  case 1:
			  MPLline(x - 5, y, x + 5, y, currentColor);
			  MPLline(x, y - 5, x, y + 5, currentColor);
			  x0 = x;
			  y0 = y;
			  break;
		  case 2:
			  a = abs(x - x0);
			  lineColor = currentColor;
		  case 3:
			  MPLline(x0 - a, y0, x0 + a, y0, lineColor);
			  break;
		  case 4:
			  b = abs(y - y0);
			  lineColor = currentColor;
			  MPLline(x0, y0 - b, x0, y0 + b, lineColor);
		  case 5:
		  	  MidpointStdEllipse(x0, y0, a, b, lineColor);
	  }
  }
};

//其实这里没有判断溢出的机制
Shape *shapes[10010];
Shape *currentShape;
int shapeIndex = 0;

//使用缓冲技术，减少因图形数量增加时出现的大量Redraw消耗
IMAGE img(640, 480);

void Refresh(){
	SetWorkingImage();
	putimage(0, 0, &img, SRCCOPY);
}

//有生之年第一次在示例之外用到工厂函数（
class ShapeFactory{
public:
//其实用枚举更好
  static int maxID;
  int shapeID = 1;
  void changeShape(){
	  if(++shapeID > maxID){
		  shapeID = 1;
	  }
  }
  Shape *createShape()
  {
	  switch (shapeID)
	  {
	  case 1:
		  return new Line();
	  case 2:
		  return new Circle();
	  case 3:
		  return new SEllipse();
	  default:
		  return new Line();
	  }
	}
};
int ShapeFactory::maxID = 3;

//初始化画布和屏幕
void Init(){
	//填充背景为白色
	SetWorkingImage(&img);
	setbkcolor(WHITE);
	cleardevice();
	//画文字提示
	settextcolor(BLACK);
	moveto(5, 5);
	outtext(TEXT("left click:draw      right click:switch shape"));
	SetWorkingImage();
	putimage(0, 0, &img, SRCCOPY);
}

//清理函数（虽然似乎不会执行）
void Clear()
{
	for (int i = 0; i < shapeIndex; ++i)
	{
		delete shapes[i];
	}
}

//主消息循环
void MessageLoop(){
	MOUSEMSG msg;
	currentShape = new Line;
	int cnt = 0;
	ShapeFactory shapeFactory;

	//缺少说明，仅仅用于调试
	while(1){
		msg = GetMouseMsg();
		switch(msg.uMsg){
			case WM_MOUSEMOVE:
				if(cnt){
					Refresh();
					currentShape->State(cnt, msg.x, msg.y);
				}
				break;
			case WM_LBUTTONDOWN:
				Refresh();
				currentShape->State(++cnt, msg.x, msg.y);
				if(++cnt >= currentShape->stateNum){
					shapes[shapeIndex++] = currentShape;
					SetWorkingImage(&img);
					currentShape->Redraw();
					SetWorkingImage();
					currentShape = shapeFactory.createShape();
					cnt = 0;
				}
				break;
			case WM_RBUTTONDOWN:
				shapeFactory.changeShape();
				currentShape = shapeFactory.createShape();
				cnt = 0;
		}
	}
}

int main()
{
	// 创建绘图窗口，大小为 640x480 像素
	initgraph(640, 480);
	//初始化
	Init();
	//开始绘制
	MessageLoop();
	//结束绘制
	_getch();              // 按任意键继续
	closegraph();          // 关闭绘图窗口
	Clear();
	return 0;
}